package top.healthylife.gateway.platform.anxinsign.api.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import top.healthylife.basics.utils.exception.BusinessException;
import top.healthylife.basics.utils.utils.SystemThreadLocal;
import top.healthylife.gateway.core.util.SystemConfigUtils;
import top.healthylife.gateway.dto.request.req.ApiReqDto;
import top.healthylife.gateway.platform.anxinsign.api.config.SystemConfig;
import top.heathylife.gateway.common.cons.SystemCons;
import top.heathylife.gateway.common.po.RecordRequestInfo;
//import cpcn.dsp.institution.api.util.Base64;
//import cpcn.dsp.institution.api.util.GUIDGenerator;
import java.net.ConnectException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;


/**
 * @author : GaoZeXi
 * @date 2021/3/22 16:57
 */
@Slf4j
public class AnxinSignUtils {

    public static Map<String, String> errorMap = new HashMap<>();

    public static final String SUCCESS_CODE = "2000";

    /**
     * 测试环境域名地址
     * 1301、1302、1304、
     * 1305、1306、1307、
     * 1308、1315、1316、
     * 1317
     */
    public static final String SIT_DOMAIN = "https://dsptest.cpcn.com.cn";
    /**
     * 生产环境域名地址
     * 1301、1302、1304、
     * 1305、1306、1307、
     * 1308、1315、1316、
     * 1317
     */
    public static final String PRO_DOMAIN = "https://data.cpcn.com.cn:9443";

    static {
        errorMap.put("0001", "参数校验失败");
        errorMap.put("1111", "机构号不存在");
        errorMap.put("2001", "系统内部错误");
        errorMap.put("2002", "验证签名失败");
        errorMap.put("2003", "解析报文错误");
        errorMap.put("2004", "获取通道信息出错");
        errorMap.put("2005", "无可用通道");
        errorMap.put("2006", "机构交易无效");
        errorMap.put("2007", "交易流水号重复");
        errorMap.put("2008", "请求通道出错");
        errorMap.put("2009", "交易异常");
        errorMap.put("2022", "系统处理超时");
        errorMap.put("2023", "并发超限");
        errorMap.put("2024", "通道并发超限");
        errorMap.put("3001", "请求报文格式不正确");
        errorMap.put("3002", "非法校验不通过");
        errorMap.put("3004", "交易类型无效");
        errorMap.put("3005", "未查询到结果");
        errorMap.put("3006", "未获得文件");
        errorMap.put("3007", "文件大小超过限制");
        errorMap.put("4001", "文件处理失败");
        errorMap.put("4002", "文件处理失败");
        errorMap.put("4004", "不正确的访问");
        errorMap.put("4009", "文件上传失败");
        errorMap.put("5002", "解密异常");
        errorMap.put("5003", "数字信封服务异常");
    }

    @SneakyThrows
    public static <T> T getApiData(String url, String txCode, ApiReqDto apiReqDto, Class<T> cls) {
        //1.获取账号信息
        SystemConfig systemConfig = getSystemConfigInfo();
        try {
            //2.初始化秘钥信息
            String keystore = systemConfig.getKeystore();
            ExtEncryptAndDecrypt extEncryptAndDecrypt = ExtEncryptAndDecrypt.initByStream(systemConfig.getPfxStream(), keystore, systemConfig.getCerStream());
            systemConfig.setExtEncryptAndDecrypt(extEncryptAndDecrypt);
        } catch (Exception e) {
            log.error("秘钥文件初始化失败", e);
            throw new BusinessException("秘钥文件初始化失败");
        }
        //3.获取当前环境的接口访问地址
        url = getUrlByEnv(systemConfig, url);

        //4.处理入参
        Map<String, String> paramMap = processRequest(apiReqDto, systemConfig, txCode);
        String requestString;
        try {
            //拼接成url地址参数
            requestString = ExtEncryptAndDecrypt.createLinkStringByGet(paramMap);
        } catch (Exception e) {
            throw new BusinessException("生成请求参数异常");
        }
        //5.调用接口
        String apiData;
        try {
            //将请求参数信息存储到线程变量
            RecordRequestInfo.recordRequest(url,requestString,null,"post");
            apiData = HttpsUtil.post(url, requestString, null);
        } catch (ConnectException exception) {
            throw new BusinessException("调用接口服务超时,请联系管理员");
        }
        //6.解密返回数据
        String respPlainText;
        String[] respMsg = apiData.split(",");
        if (respMsg.length == 1 && -1 == respMsg[0].indexOf(',')) {
            respPlainText = Base64.decodeStr(respMsg[0]);
            log.info("响应明文：" + respPlainText);
            throw new BusinessException("接口调用异常,响应明文:" + respPlainText);
        }
        if (respMsg.length == 5) {
            return decrypt(systemConfig, cls, respMsg);
        }
        return null;
    }

    @SneakyThrows
    public static <T> T decrypt(SystemConfig systemConfig, Class<T> cls, String[] respMsg) {
        ExtEncryptAndDecrypt encryptAndDecrypt = systemConfig.getExtEncryptAndDecrypt();
        String randomKeyData2 = encryptAndDecrypt.getDecryptKeyByteByRSA(respMsg[2]);
        String responsePlainText = ExtEncryptAndDecrypt.decodeByAES(randomKeyData2, respMsg[0]);
        Boolean verify = encryptAndDecrypt.verify(responsePlainText.getBytes(StandardCharsets.UTF_8), respMsg[1]);
        if (!verify) {
            throw new Exception("验证签名失败。");
        } else {
            JSONObject jsonObject = JSONObject.parseObject(responsePlainText);
            //将三方元数据存储到线程变量
            SystemThreadLocal.set(SystemCons.ORIGIN_DATA, jsonObject.toJSONString());
            String code = jsonObject.getString("Code");
            String message = jsonObject.getString("Message");
            String txCode = jsonObject.getString("TxCode");
            log.info("接口{},响应明文：{}", txCode, responsePlainText);
            if (SUCCESS_CODE.equals(code)) {
                return processResponse(responsePlainText, cls);
            }
            if (errorMap.containsKey(code)) {
                message = message != null ? message : errorMap.get(code);
                throw new BusinessException(message);
            }
            throw new BusinessException(message);
        }
    }

    /**
     * 处理解密后的文档
     *
     * @param document 解密后的文档
     * @return jsonObject
     */
    public static <T> T processResponse(String document, Class<T> cls) {
        JSONObject result = new JSONObject();
        JSONObject jsonObject = JSONObject.parseObject(document);
        jsonObject.keySet().forEach(k -> {
            //首字母小写
            String temp = k;
            String first = k.substring(0, 1);
            k = first.toLowerCase() + k.substring(1);
            result.put(k, jsonObject.get(temp));
        });
        return result.toJavaObject(cls);
    }

    /**
     * 根据环境信息获取接口访问域名
     *
     * @param systemConfig 账号信息
     * @param apiUrl       接口的访uri
     * @return 接口访问域名
     */
    public static String getUrlByEnv(SystemConfig systemConfig, String apiUrl) {
        SystemConfig.Env env = systemConfig.getEnv();
        switch (env) {
            case PRO:
                return PRO_DOMAIN + apiUrl;
            case SIT:
                return SIT_DOMAIN + apiUrl;
            default:
                //nothing
        }
        return null;
    }

    /**
     * 参数处理
     *
     * @param apiReqDto    入参
     * @param systemConfig 账号信息
     * @param txCode       apiCode
     * @return 处理后的参数
     */
    @SneakyThrows
    public static Map<String, String> processRequest(ApiReqDto apiReqDto, SystemConfig systemConfig, String txCode) {
        Map<String, Object> map = BeanUtil.beanToMap(apiReqDto);
        map.put("InstitutionID", systemConfig.getInstitutionID());
        map.put("TxCode", txCode);

        map.put("TxSN", GUIDGenerator.genGUID());
        JSONObject json = new JSONObject();
        map.keySet().forEach(k -> {
            //首字母大写
            String temp = k;
            String first = k.substring(0, 1);
            k = first.toUpperCase() + k.substring(1);
            json.put(k, map.get(temp));
        });
        String document = json.toString();
        return postProcess(document, systemConfig);
    }

    @SneakyThrows
    public static Map<String, String> postProcess(String document, SystemConfig systemConfig) {
        ExtEncryptAndDecrypt extEncryptAndDecrypt = systemConfig.getExtEncryptAndDecrypt();
        if (extEncryptAndDecrypt == null) {
            throw new Exception("证书未初始化");
        } else {
            byte[] data = document.getBytes(StandardCharsets.UTF_8);
            String randomKeyData = ExtEncryptAndDecrypt.randomHexString(32);
            byte[] randomKey = randomKeyData.getBytes(StandardCharsets.UTF_8);
            Map<String, String> paramMap = new HashMap<>(8);
            paramMap.put("message", ExtEncryptAndDecrypt.encode(document, randomKeyData));
            paramMap.put("signature", ExtEncryptAndDecrypt.bytes2hex(extEncryptAndDecrypt.sign(data)));
            paramMap.put("dgtlenvlp", extEncryptAndDecrypt.encryptByRSA(randomKey));
            paramMap.put("signSN", extEncryptAndDecrypt.requestSignSN);
            paramMap.put("encryptSN", extEncryptAndDecrypt.requestEncryptSN);
            return paramMap;
        }
    }

    /**
     * 获取账号相关信息
     *
     * @return 账号信息
     */
    public static SystemConfig getSystemConfigInfo() {
        SystemConfig systemConfig = (SystemConfig) SystemConfigUtils.get();
        log.info("当前账号信息:{}", JSONObject.toJSONString(systemConfig));
        //处理证书文件
        try {

        } catch (Exception e) {
            throw new BusinessException("证书文件获取异常,账号:{},环境:{},pfx证书key:{},cer证书key:{}", systemConfig.getInstitutionID()
                    , systemConfig.getEnv(), systemConfig.getPfxFileKey(), systemConfig.getCerFileKey());
        }
        return systemConfig;
    }

}
